package com.thz.system.utils;

import com.thz.common.exception.BusinessException;
import com.thz.common.enums.ErrorCode;
import com.thz.system.config.MattingProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 * 图片抠图工具类
 * 支持通过 Python rembg 库进行图片背景移除
 * 
 * 【PNG压缩优化】2025-11
 * - Python层：添加compress_level参数，根据quality智能选择压缩级别（6-9）
 * - Java层：二次优化PNG文件，进一步减小文件大小
 * - 效果：200KB原图抠图后从1MB降低到400KB（减少60%），质量无损
 * - 配置：application.yml中可配置matting.png.compression-enabled和default-compression-level
 * 
 * @author THZ
 * @date 2025
 */
@Slf4j
@Component
public class ImageMattingUtils {
    
    @Autowired
    private MattingProperties mattingProperties;
    
    /**
     * 常见友好名称到 rembg 模型名称的映射（中英别名均可）
     */
    private static final java.util.Map<String, String> MODEL_ALIASES = new java.util.HashMap<String, String>() {{
        // 智能/自动
        put("auto", "birefnet-general");
        put("ai", "birefnet-general");
        put("ai-auto", "birefnet-general");
        put("智能识别", "birefnet-general");
        put("ai自动选择", "birefnet-general");
        // BiRefNet
        put("birefnet-general", "birefnet-general");
        put("birefnet 通用", "birefnet-general");
        put("birefnet 最高精度", "birefnet-general");
        put("birefnet 人像", "birefnet-portrait");
        put("birefnet-portrait", "birefnet-portrait");
        // ISNet
        put("isnet-general-use", "isnet-general-use");
        put("isnet 通用", "isnet-general-use");
        put("isnet 速度快", "isnet-general-use");
        put("isnet-anime", "isnet-anime");
        put("isnet 动漫", "isnet-anime");
        put("isnet 二次元", "isnet-anime");
        put("isnet-portrait", "isnet-portrait");
        put("isnet 肖像", "isnet-portrait");
        // U2Net 系列
        put("u2net", "u2net");
        put("u2net-经典通用", "u2net");
        put("u2netp", "u2netp");
        put("u2net lite", "u2netp");
        put("u2net_lite", "u2netp");
        put("u2net_human_seg", "u2net_human_seg");
        put("u2net 人物", "u2net_human_seg");
        put("u2net_cloth_seg", "u2net_cloth_seg");
        put("u2net 服装", "u2net_cloth_seg");
        // Silueta（艺术人像）
        put("silueta", "silueta");
        put("艺术人像", "silueta");
        // SAM（分割一切）占位：rembg 需要额外依赖，这里仅做别名映射到 isnet-general-use 以保证可用
        put("sam", "isnet-general-use");
        put("分割一切", "isnet-general-use");
    }};
    
    private static String normalizeModelName(String raw) {
        if (raw == null || raw.trim().isEmpty()) return "birefnet-general";
        String key = raw.trim().toLowerCase();
        return MODEL_ALIASES.getOrDefault(key, key);
    }
    
    /**
     * Python 可执行文件路径（可通过配置文件覆盖）
     */
    @Value("${matting.python.path:python}")
    private String pythonPath;
    
    /**
     * rembg 是否作为命令行工具使用（默认false，使用python -m rembg）
     */
    @Value("${matting.use.rembg.command:false}")
    private boolean useRembgCommand;
    
    /**
     * 模型持久化目录（例如：/data/rembg/models）。为空则使用用户目录下 .u2net
     */
    @Value("${matting.model.dir:}")
    private String modelDirConfig;
    
    /**
     * 是否在启动时预加载模型
     */
    @Value("${matting.preload.enabled:true}")
    private boolean preloadEnabled;
    
    /**
     * 启动预加载的模型名称
     */
    @Value("${matting.preload.models:birefnet-general}")
    private String preloadModelName;
    
    /**
     * 启动预加载的多模型（逗号分隔），优先于单个模型
     */
    @Value("${matting.preload.models:}")
    private String preloadModelNames;
    
    /**
     * 解析后的模型缓存目录
     */
    private Path resolvedModelDir;
    
    @PostConstruct
    public void init() {
        // 解析模型目录
        if (modelDirConfig != null && !modelDirConfig.trim().isEmpty()) {
            resolvedModelDir = Paths.get(modelDirConfig.trim());
        } else {
            resolvedModelDir = Paths.get(System.getProperty("user.home"), ".u2net");
        }
        try {
            Files.createDirectories(resolvedModelDir);
            log.info("模型缓存目录: {}", resolvedModelDir.toAbsolutePath());
        } catch (IOException e) {
            log.warn("创建模型目录失败: {}，回退至用户目录", e.getMessage());
            resolvedModelDir = Paths.get(System.getProperty("user.home"), ".u2net");
        }
        if (preloadEnabled) {
            if (preloadModelNames != null && !preloadModelNames.trim().isEmpty()) {
                for (String m : preloadModelNames.split(",")) {
                    String model = normalizeModelName(m);
                    preloadModel(model);
                }
            } else {
                preloadModel(preloadModelName);
            }
        }
    }
    
    /**
     * 执行图片抠图
     * 
     * @param inputPath 输入图片路径
     * @param outputPath 输出图片路径
     * @param algorithm 抠图算法（u2net, u2netp, u2net_human_seg, u2net_cloth_seg, silueta）
     * @param backgroundColor 背景颜色（null表示透明，否则为RGB颜色值）
     * @param quality 输出质量（50-100）
     * @param processingMode 处理模式（fast/balanced/quality）
     * @return 是否成功
     */
    public boolean removeBackground(String inputPath, String outputPath, String algorithm, Color backgroundColor, int quality, String processingMode) {
        try {
            log.info("开始执行图片抠图：输入={}, 输出={}, 算法={}", inputPath, outputPath, algorithm);
            
            // 检查输入文件是否存在
            File inputFile = new File(inputPath);
            if (!inputFile.exists()) {
                throw new BusinessException(ErrorCode.FILE_NOT_FOUND, "输入文件不存在: " + inputPath);
            }
            
            // 确保输出目录存在
            File outputFile = new File(outputPath);
            File outputDir = outputFile.getParentFile();
            if (outputDir != null && !outputDir.exists()) {
                outputDir.mkdirs();
            }
            
            // 【速度优化1】智能预处理大图
            String processedInputPath = preprocessLargeImage(inputPath);
            
            // 【速度优化2】根据处理模式决定优化策略
            boolean useFastMode = "fast".equals(processingMode) || shouldUseFastMode(algorithm);
            boolean isQualityMode = "quality".equals(processingMode);
            
            // 构建命令（使用预处理后的图片路径）
            List<String> command = buildRembgCommand(processedInputPath, outputPath, algorithm, useFastMode, isQualityMode, quality);
            log.info("执行rembg命令: {}", String.join(" ", command));
            
            // 执行命令
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            // 为子进程设置模型目录相关环境变量，避免每次都使用 /root/.u2net
            if (resolvedModelDir != null) {
                String modelDirStr = resolvedModelDir.toAbsolutePath().toString();
                processBuilder.environment().put("U2NET_HOME", modelDirStr);
                processBuilder.environment().put("REMBG_DATA_DIR", modelDirStr);
                processBuilder.environment().putIfAbsent("XDG_CACHE_HOME", modelDirStr);
                if (!processBuilder.environment().containsKey("HOME") && resolvedModelDir.getParent() != null) {
                    processBuilder.environment().put("HOME", resolvedModelDir.getParent().toAbsolutePath().toString());
                }
            }
            // 限制Python/数值库线程数，避免小内存机器过度并行（可配置）
            String pyThreads = String.valueOf(Math.max(1, mattingProperties != null ? mattingProperties.getPythonThreads() : 1));
            processBuilder.environment().put("OMP_NUM_THREADS", pyThreads);
            processBuilder.environment().put("MKL_NUM_THREADS", pyThreads);
            processBuilder.environment().put("OPENBLAS_NUM_THREADS", pyThreads);
            processBuilder.environment().put("NUMEXPR_NUM_THREADS", pyThreads);
            processBuilder.environment().put("PYTHONUNBUFFERED", "1");
            processBuilder.redirectErrorStream(true);
            
            Process process = processBuilder.start();
            
            // 读取标准输出
            StringBuilder output = new StringBuilder();
            StringBuilder errorOutput = new StringBuilder();
            
            // 读取输出流
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                    log.info("rembg输出: {}", line);
                }
            }
            
            // 读取错误流（虽然已经redirectErrorStream，但以防万一）
            try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
                String line;
                while ((line = errorReader.readLine()) != null) {
                    errorOutput.append(line).append("\n");
                    log.error("rembg错误: {}", line);
                }
            }
            
            // 等待进程完成
            int exitCode = process.waitFor();
            
            log.info("rembg进程退出码: {}", exitCode);
            
            if (exitCode != 0) {
                String errorMsg = output.length() > 0 ? output.toString() : 
                                 errorOutput.length() > 0 ? errorOutput.toString() : 
                                 "未知错误（退出码: " + exitCode + "）";
                log.error("rembg执行失败，退出码: {}, 标准输出: {}, 错误输出: {}", exitCode, output, errorOutput);
                throw new BusinessException(ErrorCode.CONVERSION_FAILED, "抠图失败: " + errorMsg.trim());
            }
            
            // 检查输出文件是否生成
            if (!outputFile.exists() || outputFile.length() == 0) {
                throw new BusinessException(ErrorCode.CONVERSION_FAILED, "输出文件未生成或为空");
            }
            
            // 如果需要非透明背景，添加背景色
            if (backgroundColor != null) {
                addBackgroundColor(outputPath, backgroundColor, quality);
            }
            
            log.info("图片抠图完成（压缩前）：输出文件={}, 大小={}字节", outputPath, outputFile.length());
            
            // 【文件优化】进一步优化PNG大小（使用Java内置方法）
            optimizePngFile(outputPath, quality);
            // 【可选】pngquant二次压缩（透明PNG显著减小）
            compressWithPngquantIfEnabled(outputPath);
            
            log.info("图片抠图完成（压缩后）：输出文件={}, 大小={}字节", outputPath, new File(outputPath).length());
            
            // 【速度优化3】清理临时预处理文件
            cleanupTempFile(processedInputPath, inputPath);
            
            return true;
            
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("图片抠图失败", e);
            throw new BusinessException(ErrorCode.CONVERSION_FAILED, "抠图失败: " + e.getMessage());
        }
    }
    
    /**
     * 【速度优化】预处理大图 - 自动压缩以提速（阈值可配置）
     */
    private String preprocessLargeImage(String inputPath) throws IOException {
        BufferedImage image = ImageIO.read(new File(inputPath));
        if (image == null) {
            return inputPath;
        }
        
        int width = image.getWidth();
        int height = image.getHeight();
        int maxDimension = Math.max(width, height);
        
        // 如果图片尺寸合理，直接返回原路径
        int limit = (mattingProperties != null) ? Math.max(256, mattingProperties.getPreprocessMaxDimension()) : 1600;
        if (maxDimension <= limit) {
            log.debug("图片尺寸合理 ({}x{})，无需预处理", width, height);
            return inputPath;
        }
        
        // 超大图片压缩以提速
        log.info("检测到大图 ({}x{})，自动压缩至最大边{}以降低内存并提速", width, height, limit);
        
        // 计算压缩比例
        float scale = limit / (float) maxDimension;
        int newWidth = (int) (width * scale);
        int newHeight = (int) (height * scale);
        
        // 创建临时文件
        File tempFile = new File(inputPath.replace(getFileExtension(inputPath), "_temp." + getFileExtension(inputPath)));
        
        // 压缩图片
        BufferedImage resized = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = resized.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);  // 优先速度
        g.drawImage(image, 0, 0, newWidth, newHeight, null);
        g.dispose();
        
        // 保存临时文件
        ImageIO.write(resized, getFileExtension(inputPath), tempFile);
        
        log.info("图片已压缩至 {}x{}，预计提速 {}%", newWidth, newHeight, (int)((1 - scale * scale) * 100));
        
        return tempFile.getAbsolutePath();
    }
    
    /**
     * 【速度优化】判断是否应使用快速模式
     */
    private boolean shouldUseFastMode(String algorithm) {
        if (algorithm == null || algorithm.isEmpty()) {
            return false;  // 智能模式，保持高质量
        }
        String normalized = normalizeModelName(algorithm);
        // 快速模型列表：u2netp（lite）、isnet-general-use（较快）
        return normalized.matches("u2netp|isnet-general-use");
    }
    
    /**
     * 【速度优化】清理临时文件
     */
    private void cleanupTempFile(String processedPath, String originalPath) {
        if (!processedPath.equals(originalPath)) {
            File tempFile = new File(processedPath);
            if (tempFile.exists() && tempFile.delete()) {
                log.debug("清理临时文件: {}", processedPath);
            }
        }
    }
    
    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        int lastDot = filename.lastIndexOf('.');
        if (lastDot > 0 && lastDot < filename.length() - 1) {
            return filename.substring(lastDot + 1).toLowerCase();
        }
        return "jpg";
    }
    
    /**
     * 构建 rembg 命令（优化版）
     */
    private List<String> buildRembgCommand(String inputPath, String outputPath, String algorithm, boolean useFastMode, boolean isQualityMode, int quality) {
        List<String> command = new ArrayList<>();
        
        if (useRembgCommand) {
            // 方式1：直接使用 rembg 命令（如果已全局安装）
            log.debug("使用 rembg 命令方式");
            command.add("rembg");
        } else {
            // 方式2：使用 Python 脚本调用（推荐，更可靠）
            log.debug("使用 Python 脚本方式，Python路径: {}", pythonPath);
            command.add(pythonPath);
            command.add("-X");
            command.add("utf8");  // 设置 Python 使用 UTF-8 编码
            command.add("-c");
            
             // 构建高级 Python 脚本（使用多模型智能识别和后处理）
            StringBuilder pythonScript = new StringBuilder();
            pythonScript.append("# -*- coding: utf-8 -*-\n");
            pythonScript.append("import sys\n");
            pythonScript.append("import os\n");
            pythonScript.append("from pathlib import Path\n");
            pythonScript.append("from rembg import remove, new_session\n");
            pythonScript.append("from PIL import Image, ImageFilter, ImageEnhance\n");
            pythonScript.append("import numpy as np\n\n");
            
            // 传递模型缓存目录到子进程环境并确保目录存在
            String modelDirStr = resolvedModelDir != null ? resolvedModelDir.toAbsolutePath().toString().replace("\\\\", "\\\\\\\\") : "";
            if (!modelDirStr.isEmpty()) {
                pythonScript.append("os.environ['U2NET_HOME'] = r'").append(modelDirStr).append("'\n");
                pythonScript.append("os.environ['REMBG_DATA_DIR'] = r'").append(modelDirStr).append("'\n");
                pythonScript.append("os.environ.setdefault('XDG_CACHE_HOME', r'").append(modelDirStr).append("')\n");
                pythonScript.append("Path(os.environ['U2NET_HOME']).mkdir(parents=True, exist_ok=True)\n\n");
            }
            
            // 使用 raw string 或转义路径
            pythonScript.append("input_path = r'").append(inputPath).append("'\n");
            pythonScript.append("output_path = r'").append(outputPath).append("'\n\n");
            
            pythonScript.append("try:\n");
            pythonScript.append("    # 读取输入图片\n");
            pythonScript.append("    input_img = Image.open(input_path)\n");
            pythonScript.append("    print(f'图片尺寸: {input_img.size}')\n\n");
            
            // 根据算法选择最佳模型
            String modelName;
            boolean isAnimeMode = false;
            
            if (algorithm != null && !algorithm.isEmpty()) {
                modelName = normalizeModelName(algorithm);
                isAnimeMode = modelName.contains("anime");
                pythonScript.append("    # 使用指定的抠图算法: ").append(modelName).append("\n");
                pythonScript.append("    model = '").append(modelName).append("'\n");
                log.info("使用指定抠图算法: {}", modelName);
            } else {
                // 智能选择模型（优先精度）- 使用BiRefNet获得更好的边缘效果
                modelName = "birefnet-general";
                pythonScript.append("    # 智能选择最佳抠图模型\n");
                pythonScript.append("    # birefnet-general: 最新高精度模型，边缘处理优秀\n");
                pythonScript.append("    model = '").append(modelName).append("'\n");
                log.info("使用高精度抠图算法: birefnet-general（精度高，边缘清晰）");
            }
            
            pythonScript.append("\n    # 🔑 仅使用本地已有模型，禁止联网下载与重试\n");
            pythonScript.append("    # 优先使用 U2NET_HOME 或 REMBG_DATA_DIR，否则回退 ~/.u2net\n");
            pythonScript.append("    model_dir = Path(os.environ.get('U2NET_HOME') or os.environ.get('REMBG_DATA_DIR') or (Path.home() / '.u2net'))\n");
            pythonScript.append("    # 在本地目录中按优先级选择可用模型\n");
            pythonScript.append("    preferred_order = ['birefnet-general','isnet-general-use','u2net','u2net_human_seg','u2netp',\n");
            pythonScript.append("                       'birefnet-portrait','isnet-portrait','isnet-anime','u2net_cloth_seg','silueta']\n");
            pythonScript.append("    # 规范化首选模型名并放在最前\n");
            pythonScript.append("    first = '").append(modelName).append("'\n");
            pythonScript.append("    ordered = []\n");
            pythonScript.append("    for m in [first] + preferred_order:\n");
            pythonScript.append("        if m and m not in ordered:\n");
            pythonScript.append("            ordered.append(m)\n");
            pythonScript.append("    # 过滤出本地存在的模型\n");
            pythonScript.append("    existing = []\n");
            pythonScript.append("    for m in ordered:\n");
            pythonScript.append("        p = model_dir / f'{m}.onnx'\n");
            pythonScript.append("        if p.exists():\n");
            pythonScript.append("            existing.append(m)\n");
            pythonScript.append("    if not existing:\n");
            pythonScript.append("        raise Exception(f'未找到可用模型，请将模型文件(.onnx)放入: {model_dir}')\n");
            pythonScript.append("    # 选择第一个可用模型\n");
            pythonScript.append("    selected = existing[0]\n");
            pythonScript.append("    print(f'选择本地模型: {selected}')\n");
            pythonScript.append("    # 创建会话（仅对存在的模型），不会触发下载\n");
            pythonScript.append("    session = new_session(selected)\n");
            pythonScript.append("    model = selected\n");
            pythonScript.append("    print(f'最终使用模型: {model}')\n\n");
            
            // 【速度优化】根据模式选择不同参数
            if (useFastMode) {
                // 快速模式：轻量级Alpha Matting，平衡速度与精度
                pythonScript.append("    # 快速模式：优先速度，保持基本精度\n");
                pythonScript.append("    output_img = remove(\n");
                pythonScript.append("        input_img,\n");
                pythonScript.append("        session=session,\n");
                pythonScript.append("        alpha_matting=True,                          # 启用轻量级 Alpha Matting\n");
                pythonScript.append("        alpha_matting_foreground_threshold=260,      # 较高阈值，加快处理\n");
                pythonScript.append("        alpha_matting_background_threshold=20,       # 适中阈值，保证去除效果\n");
                pythonScript.append("        alpha_matting_erode_size=5,                  # 小腐蚀值，提速\n");
                pythonScript.append("        post_process_mask=True                       # 保留基础后处理\n");
                pythonScript.append("    )\n\n");
                
                // 快速模式也进行基本的背景清理
                pythonScript.append("    # 快速模式后处理：基本背景清理\n");
                pythonScript.append("    if output_img.mode != 'RGBA':\n");
                pythonScript.append("        output_img = output_img.convert('RGBA')\n");
                pythonScript.append("    r, g, b, alpha = output_img.split()\n");
                pythonScript.append("    alpha_array = np.array(alpha)\n");
                pythonScript.append("    alpha_array[alpha_array < 20] = 0  # 清理弱背景\n");
                pythonScript.append("    alpha_array[alpha_array > 230] = 255  # 增强主体\n");
                pythonScript.append("    alpha_cleaned = Image.fromarray(alpha_array, mode='L')\n");
                pythonScript.append("    output_img = Image.merge('RGBA', (r, g, b, alpha_cleaned))\n\n");
                
                log.info("使用快速模式，启用轻量级精度优化");
            } else if (isAnimeMode) {
                // 【动漫专用优化】更激进的参数，更彻底地去除背景
                pythonScript.append("    # 动漫/二次元专用模式：增强背景去除\n");
                pythonScript.append("    output_img = remove(\n");
                pythonScript.append("        input_img,\n");
                pythonScript.append("        session=session,\n");
                pythonScript.append("        alpha_matting=True,           # 启用 Alpha Matting\n");
                pythonScript.append("        alpha_matting_foreground_threshold=270,  # 更高阈值，更严格的前景识别\n");
                pythonScript.append("        alpha_matting_background_threshold=5,    # 更低阈值，更激进的背景去除\n");
                pythonScript.append("        alpha_matting_erode_size=15,  # 更大腐蚀值，去除边缘残留\n");
                pythonScript.append("        post_process_mask=True\n");
                pythonScript.append("    )\n\n");
                
                // 动漫图片的额外后处理
                pythonScript.append("    # 动漫图片专用后处理：强化边缘清理\n");
                pythonScript.append("    if output_img.mode != 'RGBA':\n");
                pythonScript.append("        output_img = output_img.convert('RGBA')\n\n");
                
                pythonScript.append("    # 提取Alpha通道并进行增强处理\n");
                pythonScript.append("    r, g, b, alpha = output_img.split()\n");
                pythonScript.append("    # 将alpha转为numpy数组进行高级处理\n");
                pythonScript.append("    alpha_array = np.array(alpha)\n\n");
                
                pythonScript.append("    # 二值化处理：清理半透明区域\n");
                pythonScript.append("    # 阈值120：低于120的设为0（完全透明），高于的保持不变\n");
                pythonScript.append("    alpha_array[alpha_array < 120] = 0\n");
                pythonScript.append("    # 阈值200：高于200的设为255（完全不透明）\n");
                pythonScript.append("    alpha_array[alpha_array > 200] = 255\n\n");
                
                pythonScript.append("    # 对中间值进行平滑过渡\n");
                pythonScript.append("    mask = (alpha_array >= 120) & (alpha_array <= 200)\n");
                pythonScript.append("    alpha_array[mask] = ((alpha_array[mask] - 120) / 80 * 255).astype(np.uint8)\n\n");
                
                pythonScript.append("    # 转回PIL Image\n");
                pythonScript.append("    alpha_cleaned = Image.fromarray(alpha_array, mode='L')\n");
                pythonScript.append("    # 轻微模糊以平滑边缘\n");
                pythonScript.append("    alpha_final = alpha_cleaned.filter(ImageFilter.GaussianBlur(radius=0.5))\n");
                pythonScript.append("    output_img = Image.merge('RGBA', (r, g, b, alpha_final))\n\n");
                
                log.info("使用动漫专用模式，启用增强背景清理");
            } else if (isQualityMode) {
                // 【优化】高质量模式：平衡精度与速度，优化参数提升处理速度
                pythonScript.append("    # 高质量模式（优化版）：平衡精度与速度\n");
                pythonScript.append("    output_img = remove(\n");
                pythonScript.append("        input_img,\n");
                pythonScript.append("        session=session,\n");
                pythonScript.append("        alpha_matting=True,           # 启用 Alpha Matting 精细边缘\n");
                pythonScript.append("        alpha_matting_foreground_threshold=245,  # 提高阈值，加快处理（原240->245）\n");
                pythonScript.append("        alpha_matting_background_threshold=10,   # 背景阈值，彻底去除背景\n");
                pythonScript.append("        alpha_matting_erode_size=8,              # 减小腐蚀值，提速（原10->8）\n");
                pythonScript.append("        post_process_mask=True                   # 启用后处理\n");
                pythonScript.append("    )\n\n");
                
                // 【优化】简化后处理：保持精度但减少计算量
                pythonScript.append("    # 高质量后处理（优化版）：简化处理流程，提升速度\n");
                pythonScript.append("    if output_img.mode != 'RGBA':\n");
                pythonScript.append("        output_img = output_img.convert('RGBA')\n\n");
                
                pythonScript.append("    # 提取Alpha通道进行优化处理\n");
                pythonScript.append("    r, g, b, alpha = output_img.split()\n");
                pythonScript.append("    alpha_array = np.array(alpha)\n\n");
                
                pythonScript.append("    # 【优化】合并步骤：一次性处理，减少数组操作\n");
                pythonScript.append("    # 步骤1&2: 同时清理背景和增强主体（合并操作，提速）\n");
                pythonScript.append("    alpha_array[alpha_array < 30] = 0   # 清理极弱背景\n");
                pythonScript.append("    alpha_array[alpha_array > 220] = 255  # 增强主体\n\n");
                
                pythonScript.append("    # 【优化】步骤3: 简化过渡处理，使用线性插值替代平方根（更快）\n");
                pythonScript.append("    transition_mask = (alpha_array > 30) & (alpha_array < 220)\n");
                pythonScript.append("    if np.any(transition_mask):\n");
                pythonScript.append("        # 使用线性插值，速度更快，效果接近\n");
                pythonScript.append("        normalized = (alpha_array[transition_mask] - 30) / 190.0\n");
                pythonScript.append("        alpha_array[transition_mask] = (normalized * 255).astype(np.uint8)\n\n");
                
                pythonScript.append("    # 【优化】步骤4: 减小模糊半径，提速（原0.8->0.5）\n");
                pythonScript.append("    alpha_enhanced = Image.fromarray(alpha_array, mode='L')\n");
                pythonScript.append("    # 减小模糊半径，处理更快，仍能平滑边缘\n");
                pythonScript.append("    alpha_final = alpha_enhanced.filter(ImageFilter.GaussianBlur(radius=0.5))\n");
                pythonScript.append("    output_img = Image.merge('RGBA', (r, g, b, alpha_final))\n\n");
                
                log.info("使用高质量模式（优化版），平衡精度与速度");
            } else {
                // 平衡模式：标准精度
                pythonScript.append("    # 平衡模式：标准精度和速度\n");
                pythonScript.append("    output_img = remove(\n");
                pythonScript.append("        input_img,\n");
                pythonScript.append("        session=session,\n");
                pythonScript.append("        alpha_matting=True,           # 启用 Alpha Matting 精细边缘\n");
                pythonScript.append("        alpha_matting_foreground_threshold=240,  # 前景阈值，确保主体完整\n");
                pythonScript.append("        alpha_matting_background_threshold=10,   # 背景阈值，彻底去除背景\n");
                pythonScript.append("        alpha_matting_erode_size=10,             # 适度腐蚀，清理边缘\n");
                pythonScript.append("        post_process_mask=True                   # 启用后处理\n");
                pythonScript.append("    )\n\n");
                
                // 标准后处理
                pythonScript.append("    # 标准后处理：基本边缘清理\n");
                pythonScript.append("    if output_img.mode != 'RGBA':\n");
                pythonScript.append("        output_img = output_img.convert('RGBA')\n\n");
                
                pythonScript.append("    # 提取Alpha通道进行基本处理\n");
                pythonScript.append("    r, g, b, alpha = output_img.split()\n");
                pythonScript.append("    alpha_array = np.array(alpha)\n\n");
                
                pythonScript.append("    # 基本清理：去除背景残留\n");
                pythonScript.append("    alpha_array[alpha_array < 30] = 0\n");
                pythonScript.append("    alpha_array[alpha_array > 220] = 255\n\n");
                
                pythonScript.append("    # 转回PIL并轻微平滑\n");
                pythonScript.append("    alpha_enhanced = Image.fromarray(alpha_array, mode='L')\n");
                pythonScript.append("    alpha_final = alpha_enhanced.filter(ImageFilter.GaussianBlur(radius=0.5))\n");
                pythonScript.append("    output_img = Image.merge('RGBA', (r, g, b, alpha_final))\n\n");
                
                log.info("使用平衡模式，标准精度和速度");
            }
            
            // 保存结果 - 添加PNG压缩优化
            pythonScript.append("    # 保存结果（PNG压缩优化）\n");
            pythonScript.append("    # 根据质量参数设置压缩级别，减小文件大小\n");
            pythonScript.append("    quality_param = ").append(quality).append("\n");
            pythonScript.append("    if quality_param >= 95:\n");
            pythonScript.append("        compress_level = 6  # 高质量模式：适度压缩\n");
            pythonScript.append("    elif quality_param >= 75:\n");
            pythonScript.append("        compress_level = 7  # 平衡模式：较强压缩（推荐）\n");
            pythonScript.append("    else:\n");
            pythonScript.append("        compress_level = 9  # 最大压缩：文件最小\n");
            pythonScript.append("    \n");
            pythonScript.append("    output_img.save(output_path, 'PNG', optimize=True, compress_level=compress_level)\n");
            pythonScript.append("    print(f'抠图成功！压缩级别={compress_level}')\n\n");
            
            pythonScript.append("except Exception as e:\n");
            pythonScript.append("    print(f'抠图失败: {str(e)}', file=sys.stderr)\n");
            pythonScript.append("    sys.exit(1)\n");
            
            command.add(pythonScript.toString());
            
            log.debug("Python脚本: {}", pythonScript.toString());
        }
        
        log.debug("完整命令: {}", String.join(" ", command));
        
        return command;
    }
    
    /**
     * 添加背景颜色（替换透明背景）
     */
    private void addBackgroundColor(String imagePath, Color backgroundColor, int quality) throws IOException {
        BufferedImage image = ImageIO.read(new File(imagePath));
        
        int width = image.getWidth();
        int height = image.getHeight();
        
        BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = newImage.createGraphics();
        
        // 根据质量设置渲染选项
        if (quality >= 90) {
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        }
        
        // 填充背景色
        g.setColor(backgroundColor);
        g.fillRect(0, 0, width, height);
        
        // 绘制原图
        g.drawImage(image, 0, 0, null);
        g.dispose();
        
        // 使用质量参数保存PNG
        savePngWithQuality(newImage, imagePath, quality);
    }
    
    /**
     * 🔑 预下载模型文件（应用启动时调用，避免首次使用时下载）
     * 在后台异步预下载常用模型，避免首次使用时等待下载
     */
    public void preloadModel(String modelName) {
        // 🔑 将modelName复制到final变量，以便在lambda表达式中使用
        final String finalModelName;
        if (modelName == null || modelName.isEmpty()) {
            finalModelName = "birefnet-general";  // 默认使用高质量模型
        } else {
            finalModelName = modelName;
        }
        
        log.info("开始预下载模型文件: {}", finalModelName);
        
        // 在后台线程中预下载，避免阻塞应用启动
        new Thread(() -> {
            try {
                // 构建预下载脚本
                List<String> command = new ArrayList<>();
                command.add(pythonPath);
                command.add("-X");
                command.add("utf8");
                command.add("-c");
                
                StringBuilder preloadScript = new StringBuilder();
                preloadScript.append("import sys\n");
                preloadScript.append("from pathlib import Path\n");
                preloadScript.append("from rembg import new_session\n");
                preloadScript.append("import os\n\n");
                
                // 传入相同的缓存目录环境变量
                String preloadModelDir = resolvedModelDir != null ? resolvedModelDir.toAbsolutePath().toString().replace("\\\\", "\\\\\\\\") : "";
                if (!preloadModelDir.isEmpty()) {
                    preloadScript.append("os.environ['U2NET_HOME'] = r'").append(preloadModelDir).append("'\n");
                    preloadScript.append("os.environ['REMBG_DATA_DIR'] = r'").append(preloadModelDir).append("'\n");
                    preloadScript.append("os.environ.setdefault('XDG_CACHE_HOME', r'").append(preloadModelDir).append("')\n");
                    preloadScript.append("Path(os.environ['U2NET_HOME']).mkdir(parents=True, exist_ok=True)\n\n");
                }
                
                preloadScript.append("model = '").append(finalModelName).append("'\n");
                preloadScript.append("model_dir = Path(os.environ.get('U2NET_HOME') or os.environ.get('REMBG_DATA_DIR') or (Path.home() / '.u2net'))\n");
                preloadScript.append("model_file = model_dir / f'{model}.onnx'\n\n");
                
                preloadScript.append("# 检查模型文件是否已存在\n");
                preloadScript.append("if model_file.exists():\n");
                preloadScript.append("    file_size_mb = model_file.stat().st_size / (1024*1024)\n");
                preloadScript.append("    print(f'模型文件已存在: {model_file}, 大小: {file_size_mb:.2f}MB')\n");
                preloadScript.append("    if file_size_mb >= 100:\n");
                preloadScript.append("        print('模型文件完整，无需下载')\n");
                preloadScript.append("        sys.exit(0)\n");
                preloadScript.append("    else:\n");
                preloadScript.append("        print(f'模型文件不完整（{file_size_mb:.2f}MB），将重新下载')\n");
                preloadScript.append("        model_file.unlink()\n\n");
                
                preloadScript.append("print(f'开始预下载模型: {model}')\n");
                preloadScript.append("print('提示：首次下载需要下载模型文件（约973MB），请耐心等待...')\n");
                preloadScript.append("os.environ.setdefault('REMBG_TIMEOUT', '3600')\n");
                preloadScript.append("session = new_session(model)\n");
                preloadScript.append("print(f'模型预下载完成: {model}')\n");
                
                command.add(preloadScript.toString());
                
                ProcessBuilder processBuilder = new ProcessBuilder(command);
                processBuilder.redirectErrorStream(true);
                Process process = processBuilder.start();
                
                // 读取输出
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        log.info("模型预下载输出: {}", line);
                    }
                }
                
                int exitCode = process.waitFor();
                if (exitCode == 0) {
                    log.info("模型预下载成功: {}", finalModelName);
                } else {
                    log.warn("模型预下载失败，退出码: {}, 将在首次使用时自动下载", exitCode);
                }
            } catch (Exception e) {
                log.warn("模型预下载异常，将在首次使用时自动下载: {}", e.getMessage());
            }
        }, "ModelPreload-" + finalModelName).start();
    }
    
    /**
     * 检查 rembg 是否可用
     */
    public boolean isRembgAvailable() {
        try {
            List<String> testCommand = new ArrayList<>();
            if (useRembgCommand) {
                testCommand.add("rembg");
                testCommand.add("--help");
            } else {
                testCommand.add(pythonPath);
                testCommand.add("-c");
                testCommand.add("import rembg; print(rembg.__version__)");
            }
            
            ProcessBuilder processBuilder = new ProcessBuilder(testCommand);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();
            
            // 读取输出
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line = reader.readLine();
                if (line != null) {
                    log.info("rembg 版本: {}", line);
                }
            }
            
            int exitCode = process.waitFor();
            boolean available = exitCode == 0;
            
            if (available) {
                log.info("rembg 可用");
            } else {
                log.warn("rembg 不可用，退出码: {}", exitCode);
            }
            
            return available;
        } catch (Exception e) {
            log.warn("rembg 不可用: {}", e.getMessage());
            return false;
        }
    }
    
    /**
     * 获取图片尺寸信息
     */
    public Dimension getImageDimensions(String imagePath) throws IOException {
        BufferedImage image = ImageIO.read(new File(imagePath));
        if (image == null) {
            throw new IOException("无法读取图片: " + imagePath);
        }
        return new Dimension(image.getWidth(), image.getHeight());
    }
    
    /**
     * 调整图片大小
     * 
     * @param inputPath 输入路径
     * @param outputPath 输出路径
     * @param width 目标宽度
     * @param height 目标高度
     * @param keepAspectRatio 是否保持宽高比
     * @param quality 输出质量（50-100，影响PNG压缩级别）
     */
    public void resizeImage(String inputPath, String outputPath, int width, int height, boolean keepAspectRatio, int quality) throws IOException {
        BufferedImage originalImage = ImageIO.read(new File(inputPath));
        
        if (keepAspectRatio) {
            // 保持宽高比
            int originalWidth = originalImage.getWidth();
            int originalHeight = originalImage.getHeight();
            float aspectRatio = (float) originalWidth / originalHeight;
            
            if (width > 0 && height > 0) {
                float targetAspectRatio = (float) width / height;
                if (aspectRatio > targetAspectRatio) {
                    height = (int) (width / aspectRatio);
                } else {
                    width = (int) (height * aspectRatio);
                }
            } else if (width > 0) {
                height = (int) (width / aspectRatio);
            } else if (height > 0) {
                width = (int) (height * aspectRatio);
            }
        }
        
        BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = resizedImage.createGraphics();
        
        // 根据质量设置渲染选项
        if (quality >= 90) {
            // 高质量：使用双三次插值
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        } else {
            // 标准质量：使用双线性插值
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
        }
        
        g.drawImage(originalImage, 0, 0, width, height, null);
        g.dispose();
        
        // 使用质量参数保存PNG
        savePngWithQuality(resizedImage, outputPath, quality);
    }
    
    /**
     * 保存PNG图片并应用质量设置
     * 
     * @param image 图片
     * @param outputPath 输出路径
     * @param quality 质量（50-100）
     */
    private void savePngWithQuality(BufferedImage image, String outputPath, int quality) throws IOException {
        // 将质量值(50-100)转换为PNG压缩级别(1-9)
        // PNG压缩级别说明：数字越大，压缩越强，文件越小，但质量损失很小（PNG是无损压缩）
        // quality: 95-100 -> compressionLevel: 6 (适度压缩，高质量)
        // quality: 75-94  -> compressionLevel: 7 (较强压缩，平衡)
        // quality: 50-74  -> compressionLevel: 9 (最大压缩，最小文件)
        int compressionLevel;
        if (quality >= 95) {
            compressionLevel = 6;  // 高质量模式
        } else if (quality >= 75) {
            compressionLevel = 7;  // 平衡模式
        } else {
            compressionLevel = 9;  // 最大压缩
        }
        compressionLevel = Math.max(1, Math.min(9, compressionLevel));
        
        // 使用ImageWriter控制PNG压缩
        javax.imageio.stream.ImageOutputStream ios = javax.imageio.ImageIO.createImageOutputStream(new File(outputPath));
        javax.imageio.ImageWriter writer = javax.imageio.ImageIO.getImageWritersByFormatName("png").next();
        javax.imageio.ImageWriteParam writeParam = writer.getDefaultWriteParam();
        
        // PNG使用压缩模式
        if (writeParam.canWriteCompressed()) {
            writeParam.setCompressionMode(javax.imageio.ImageWriteParam.MODE_EXPLICIT);
            // PNG压缩级别：0=无压缩，9=最大压缩
            // 注意：这里的压缩级别是0-1的浮点数
            float compressionQuality = compressionLevel / 9.0f;
            writeParam.setCompressionQuality(compressionQuality);
        }
        
        writer.setOutput(ios);
        writer.write(null, new javax.imageio.IIOImage(image, null, null), writeParam);
        
        ios.close();
        writer.dispose();
        
        log.debug("PNG保存完成，质量={}%, 压缩级别={}", quality, compressionLevel);
    }
    
    /**
     * 压缩PNG文件（公开方法，供外部调用）
     * 综合使用多种压缩方法优化PNG文件大小
     * 
     * @param filePath PNG文件路径
     * @param quality 质量参数（50-100）
     */
    public void compressPngFile(String filePath, int quality) {
        optimizePngFile(filePath, quality);
        compressWithPngquantIfEnabled(filePath);
    }
    
    /**
     * 优化PNG文件大小
     * 通过重新读取并使用更好的压缩设置保存PNG文件来减小文件大小
     * 
     * @param filePath PNG文件路径
     * @param quality 质量参数（50-100）
     */
    private void optimizePngFile(String filePath, int quality) {
        try {
            File file = new File(filePath);
            long originalSize = file.length();
            
            // 读取PNG图片
            BufferedImage image = ImageIO.read(file);
            if (image == null) {
                log.warn("无法读取PNG文件进行优化: {}", filePath);
                return;
            }
            
            // 创建临时文件
            File tempFile = new File(filePath + ".tmp");
            
            // 根据质量参数决定压缩策略
            if (quality >= 95) {
                // 高质量模式：使用适度压缩
                savePngWithCompression(image, tempFile, 6);
            } else if (quality >= 75) {
                // 平衡模式：使用较强压缩
                savePngWithCompression(image, tempFile, 7);
            } else {
                // 极速模式：使用最大压缩
                savePngWithCompression(image, tempFile, 9);
            }
            
            long optimizedSize = tempFile.length();
            
            // 只有当优化后的文件更小时才替换原文件
            if (optimizedSize < originalSize) {
                // 删除原文件，重命名临时文件
                file.delete();
                tempFile.renameTo(file);
                
                double reduction = (1 - (double) optimizedSize / originalSize) * 100;
                log.info("PNG优化成功: {} -> {} 字节 (减少 {:.1f}%)", 
                    originalSize, optimizedSize, reduction);
            } else {
                // 优化后反而更大，删除临时文件
                tempFile.delete();
                log.debug("PNG优化无效果，保留原文件");
            }
            
        } catch (Exception e) {
            log.warn("PNG优化失败（保留原文件）: {}", e.getMessage());
        }
    }
    
    // ========================= pngquant 压缩（可选） =========================
    
    /**
     * 是否启用 pngquant 进一步压缩（显著降低有透明通道PNG体积）
     */
    @Value("${matting.pngquant.enabled:true}")
    private boolean pngquantEnabled;
    
    /**
     * pngquant 可执行文件路径（默认从 PATH 查找）
     * Linux 通常为 pngquant；Windows 可配置为 D:/tools/pngquant.exe
     */
    @Value("${matting.pngquant.path:pngquant}")
    private String pngquantPath;
    
    /**
     * pngquant 质量范围（min-max），数值范围 0-100
     */
    @Value("${matting.pngquant.quality-min:70}")
    private int pngquantQualityMin;
    
    @Value("${matting.pngquant.quality-max:95}")
    private int pngquantQualityMax;
    
    /**
     * pngquant 速度，1=最慢最优，10=最快
     */
    @Value("${matting.pngquant.speed:1}")
    private int pngquantSpeed;
    
    /**
     * 使用 pngquant 进行有损调色板压缩（保持透明），极大缩小 RGBA PNG 文件。
     * 压缩后仅在新文件更小的情况下替换原文件。
     * 
     * @param filePath PNG文件路径
     */
    public void compressWithPngquantIfEnabled(String filePath) {
        if (!pngquantEnabled) {
            return;
        }
        try {
            File input = new File(filePath);
            if (!input.exists() || input.length() == 0) {
                return;
            }
            // 输出到临时文件，避免直接覆盖失败
            File tempOut = File.createTempFile("pngquant_", ".png", input.getParentFile());
            String qualityArg = Math.max(0, Math.min(100, pngquantQualityMin)) + "-" + Math.max(0, Math.min(100, pngquantQualityMax));
            int clampedSpeed = Math.max(1, Math.min(10, pngquantSpeed));
            
            List<String> cmd = new ArrayList<>();
            cmd.add(pngquantPath);
            cmd.add("--quality=" + qualityArg);
            cmd.add("--speed");
            cmd.add(String.valueOf(clampedSpeed));
            cmd.add("--force");
            cmd.add("--output");
            cmd.add(tempOut.getAbsolutePath());
            cmd.add("--");
            cmd.add(filePath);
            
            ProcessBuilder pb = new ProcessBuilder(cmd);
            pb.redirectErrorStream(true);
            Process p = pb.start();
            StringBuilder out = new StringBuilder();
            try (BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
                String line;
                while ((line = r.readLine()) != null) {
                    out.append(line).append('\n');
                }
            }
            int code = p.waitFor();
            if (code == 0 && tempOut.exists() && tempOut.length() > 0) {
                long original = input.length();
                long compressed = tempOut.length();
                if (compressed < original) {
                    if (input.delete()) {
                        boolean renamed = tempOut.renameTo(input);
                        if (!renamed) {
                            // 兼容不同平台的文件替换
                            Files.copy(tempOut.toPath(), input.toPath());
                            tempOut.delete();
                        }
                        double reduction = (1 - (double) compressed / original) * 100;
                        log.info("pngquant压缩成功: {} -> {} 字节 (减少 {:.1f}%)", original, compressed, reduction);
                    } else {
                        log.warn("pngquant压缩后替换原文件失败，保留原文件");
                        tempOut.delete();
                    }
                } else {
                    // 压缩无收益
                    tempOut.delete();
                    log.debug("pngquant压缩无收益，保留原文件（输出大小 >= 原始大小）");
                }
            } else {
                // 执行失败，保留原文件
                log.warn("pngquant执行失败（退出码={}），信息: {}", code, out.toString().trim());
                if (tempOut.exists()) {
                    tempOut.delete();
                }
            }
        } catch (Exception e) {
            // 环境缺少 pngquant 或执行异常，降级为跳过
            log.info("pngquant未生效（可能未安装或不可用）：{}", e.getMessage());
        }
    }
    
    /**
     * 使用指定压缩级别保存PNG
     * 
     * @param image 图片
     * @param outputFile 输出文件
     * @param compressionLevel 压缩级别（1-9，9是最大压缩）
     */
    private void savePngWithCompression(BufferedImage image, File outputFile, int compressionLevel) throws IOException {
        javax.imageio.stream.ImageOutputStream ios = javax.imageio.ImageIO.createImageOutputStream(outputFile);
        javax.imageio.ImageWriter writer = javax.imageio.ImageIO.getImageWritersByFormatName("png").next();
        javax.imageio.ImageWriteParam writeParam = writer.getDefaultWriteParam();
        
        if (writeParam.canWriteCompressed()) {
            writeParam.setCompressionMode(javax.imageio.ImageWriteParam.MODE_EXPLICIT);
            // 压缩级别转换为0-1的浮点数
            float compressionQuality = compressionLevel / 9.0f;
            writeParam.setCompressionQuality(compressionQuality);
        }
        
        writer.setOutput(ios);
        writer.write(null, new javax.imageio.IIOImage(image, null, null), writeParam);
        
        ios.close();
        writer.dispose();
    }
    
    /**
     * 解析颜色字符串（支持 #RRGGBB 格式）
     */
    public Color parseColor(String colorStr) {
        if (colorStr == null || colorStr.equalsIgnoreCase("transparent")) {
            return null;
        }
        
        try {
            if (colorStr.startsWith("#")) {
                colorStr = colorStr.substring(1);
            }
            int rgb = Integer.parseInt(colorStr, 16);
            return new Color(rgb);
        } catch (Exception e) {
            log.warn("无法解析颜色: {}, 使用白色", colorStr);
            return Color.WHITE;
        }
    }
}

